/***************************************************************************
 *   Copyright (C) 2007 by Alexandre Masselot,,,   *
 *   alex@alex-laptop   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef BOLSPECTRUMLCMSMS_H
#define BOLSPECTRUMLCMSMS_H

#ifdef HAVE_BOOST_SERIALIZATION
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/split_member.hpp>
#include "spectrumpeakrefdualptr.h"
#else
#include <vector> // included by serialisers bellow
#include <string>
#endif

#ifdef HAVE_PROTOBUF
#ifdef _MSC_VER
// we don't create DLLs and thus don't need to pay attention to this warning
#pragma warning( disable: 4251 )
#endif
#include "runlcmsms.pb.h"
#endif

#include <boost/shared_ptr.hpp>

#include "peaklcms.h"
#include "spectrumpeakref.h"
#include "spectrumlcms.h"

namespace bol {

  //@TODO see if we might force a templated class to SpectrumPeakRef
  //@TODO do we really need a shared pointer at this position
  typedef boost::shared_ptr<SpectrumPeakRef<PeakIntensity> > SpectrumPeakRefPtr;

  /**
   * Contains a fragmentation (level >=2) spectrum
   * precursor info (with multiple possible moz if needed)
   * description
   * a pointer towards a 

   * There is a redudency between the info contained in the inheritance PeakLcms and the pointer SpectrumPeakRefPtr
	@author Alexandre Masselot,,, <alex@alex-laptop>
   */
  class SpectrumLcmsms : public Spectrum<PeakIntensity*>, public PeakLcms
  {
  private:
      string m_description;
      vector<double> m_extraMoz;
      SpectrumPeakRefPtr m_spectrumPeakRef;

      double m_collisionEnergy;

#ifdef HAVE_BOOST_SERIALIZATION
      // *** BOOST serialisation code ***

      template<class Archive>
          void save(Archive & ar, const unsigned int version) const
      {
        const Spectrum<PeakIntensity *> *spectrum = m_spectrumPeakRef ? m_spectrumPeakRef->spectrum() : NULL;
        const PeakIntensity *peak =  m_spectrumPeakRef ? m_spectrumPeakRef->peak() : NULL;

        ar << boost::serialization::make_nvp("refspectrum",spectrum);
        ar << boost::serialization::make_nvp("refpeak",peak);
      }

      template<class Archive>
          void load(Archive & ar, const unsigned int version)
      {
        Spectrum<PeakIntensity *> *spectrum;
        PeakIntensity *peak;

        ar >> boost::serialization::make_nvp("refspectrum", spectrum);
        ar >> boost::serialization::make_nvp("refpeak", peak);

        if (spectrum || peak) { // no need to load a ref if both pointers are null
          SpectrumPeakRefDualPtr<PeakIntensity> ptr = new SpectrumPeakRefDualPtr<PeakIntensity> (spectrum, peak);
          m_spectrumPeakRef = ptr;
        }
      }


      friend class boost::serialization::access; // gives access to serialisator
      template<class Archive> void serialize ( Archive & ar, const unsigned int version ) const
      {
        // base class serialisation - warning, as there are several variable, the order is critical...
        // ... well not for XML. But for most other archive types.
        ar & boost::serialization::make_nvp("PeakLcms", boost::serialization::base_object<PeakLcms> ( *this ));

        ar & boost::serialization::make_nvp("description", m_description);
        ar & boost::serialization::make_nvp("extraMoz", m_extraMoz);
        ar & boost::serialization::make_nvp("collisionenergy", m_collisionEnergy);

        // Ref
        boost::serialization::split_member(ar, *this, version);

        // the peaks
        ar & boost::serialization::make_nvp("Spectrum", boost::serialization::base_object<Spectrum<PeakIntensity*> > ( *this ));
      }
#endif
    public:
    SpectrumLcmsms(const double m, const Pchargemask charges, const double i=0):PeakLcms(m, charges, i), m_collisionEnergy(0.), Spectrum<PeakIntensity*>(){
        m_description="";

        #ifdef HAVE_BOOST_SERIALIZATION
        // trick to make serialisation more easy
        SpectrumPeakRefPtr ptr(new SpectrumPeakRefDualPtr<PeakIntensity> (NULL, NULL));
        m_spectrumPeakRef = ptr;
        #endif
      }

      ~SpectrumLcmsms() {}

    void setDescription(const string& theValue) {
        m_description = theValue;
    }


    string description() const {
        return m_description;
    }


    void setCollisionEnergy(double theValue){
      m_collisionEnergy = theValue;
    }
    double collisionEnergy() const {
      return m_collisionEnergy;
    }

    /**
    * extra m/z can be stored (for example isotopic delta if tolerance is very small)
    * @return a vector of double
    */
    vector< double >& extraMoz() {
        return m_extraMoz;
    }

    friend ostream& operator<<(ostream& out, const SpectrumLcmsms& sp);

    /**
    * set the refernce towards a spectrum (typically mslevel=1) and a peak index into this spectrum)
    * @param theValue
    */
    void setSpectrumPeakRef(const SpectrumPeakRefPtr& theValue) {
        m_spectrumPeakRef = theValue;
    }


    SpectrumPeakRefPtr spectrumPeakRef() const {
        return m_spectrumPeakRef;
    }


  };

#ifdef HAVE_PROTOBUF
  namespace pb_common {
    static inline bool save(protobuf::RunLcmsms::Spectrum& pb, class SpectrumLcmsms& s) {
      pb.set_description(s.description());
      if ((! isnan(s.retentionTime())) && s.retentionTime() >= 0.)
        pb.set_retentiontime(s.retentionTime());
      if (s.refid() >= 0)
        pb.set_refid(s.refid());
      if ((! isnan(s.collisionEnergy())) && s.collisionEnergy() != 0.)
        pb.set_collisionenergy(s.collisionEnergy());
      for (vector< double >::const_iterator i = s.extraMoz().begin(); i !=  s.extraMoz().end(); i++) {
        pb.add_extramoz( *i );
      }

      // do we have a reference ?
      if ((s.spectrumPeakRef() != NULL) && 
          ((s.spectrumPeakRef()->spectrum() != NULL) || (s.spectrumPeakRef()->peak() != NULL))) {
        // for now, put invalid references
        pb.mutable_reference()->clear_peak(); // defaults are "-1"
        pb.mutable_reference()->clear_spectrum();
      } else {
        // no references
        pb.clear_reference();
      }

      *(pb.mutable_parent()) << static_cast< PeakLcms & > (s);
      return true;
    }
  }

  static inline bool protobufwithmap(protobuf::RunLcmsms::Spectrum& pb, class SpectrumLcmsms& s, Ptr2IndexMapPtr spidx) {
    pb_common::save(pb, s);
    protobufwithmap(pb, static_cast< Spectrum<PeakIntensity*>& > (s), spidx);
    return true;
  }

  static inline bool operator<< (protobuf::RunLcmsms::Spectrum& pb, class SpectrumLcmsms& s) {
    pb_common::save(pb, s);
    pb << static_cast< Spectrum<PeakIntensity*>& > (s);
    return true;
  }

  static inline bool operator>> (const protobuf::RunLcmsms::Spectrum& pb, class SpectrumLcmsms& s) {
    if (pb.has_description())
      s.setDescription(pb.description());
    if (pb.has_retentiontime())
      s.setRetentionTime(pb.retentiontime());
    if (pb.has_refid())
      s.setRefid(pb.refid());
    if (pb.has_collisionenergy())
      s.setCollisionEnergy(pb.collisionenergy());

    s.extraMoz().resize(pb.extramoz_size());
    for (int i = 0; i < pb.extramoz_size(); i++) {
      s.extraMoz()[i] = pb.extramoz(i);
    }
    pb.parent() >> static_cast< PeakLcms& > (s);
    pb >> static_cast< Spectrum<PeakIntensity*>& > (s);
    return true;
  }
#endif

}

#ifdef HAVE_BOOST_SERIALIZATION
#if defined(WIN32) && (BOOST_VERSION < 103600)
// other wise Visual Studio spits error messages
#include <boost/serialization/extended_type_info_typeid.hpp>
#endif
BOOST_CLASS_EXPORT_GUID(bol::SpectrumLcmsms , "SpectrumLcmsms") // register GUID
#endif
        
#endif
